package cn.deschen.eshop.common.exception;

import cn.deschen.eshop.common.api.CommonRes;
import cn.deschen.eshop.common.enums.GlobalEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @Author deschen
 * @Create 2019/9/16
 * @Description 统一异常处理
 * @Since 1.0.0
 */
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {


    @ExceptionHandler({Throwable.class})
    public CommonRes handleThrowable(HttpServletRequest request, Throwable e) {
        log.error("错误请求url = {}, exception = {}", request.getRequestURI(), e.getMessage());
        return CommonRes.failure(GlobalEnum.SERVICE_ERROR);
    }

    /**
     * 自定义异常
     * @param request 请求体
     * @param e 自定义全局异常
     * @return 返回结果集
     */
    @ExceptionHandler({GlobalException.class})
    public CommonRes<Object> handleGlobalException(HttpServletRequest request, GlobalException e) {
        log.error("错误请求url = {}, exception = {}", request.getRequestURI(), e);
        return CommonRes.failure(e.getCode(), e.getMessage());
    }

    /**
     * 数据库内容重复异常
     * @param request 请求体
     * @param e 数据库重复异常
     * @return 返回结果集
     */
    @ExceptionHandler({DuplicateKeyException.class})
    public CommonRes<Object> handleDuplicateKeyException(HttpServletRequest request, DuplicateKeyException e) {
        log.error("错误请求url = {}, exception = {}", request.getRequestURI(), e);
        return CommonRes.failure(GlobalEnum.CONTENT_REPETITION_ERROR);
    }


    /**
     * 异常 对应 注解
     * MethodArgumentNotValidException  方法参数 @RequestBody @Valid\@Validated
     * BindException  方法参数 @Validated
     * 这两个注解都是实现JSR-303规范，不同的是@Validated是spring的注解支持groups以及可以用在spring mvc处理器的方法级别入参验证 ，@Valid是Javax提供的注解，可以支持多个bean嵌套验证。
     * @param request 请求体
     * @param e 校验器异常
     * @return 返回结果集
     */
    @ExceptionHandler({MethodArgumentNotValidException.class, BindException.class})
    public CommonRes handleValidException(HttpServletRequest request, Exception e) {
        log.error("错误请求url = {}, exception = {}", request.getRequestURI(), e);
        BindingResult bindingResult = null;
        String message = null;

        if (e instanceof BindException) {
            bindingResult = ((BindException) e).getBindingResult();
        }
        if (e instanceof MethodArgumentNotValidException) {
            bindingResult = ((MethodArgumentNotValidException) e).getBindingResult();
        }

        if (bindingResult.hasErrors()) {
            message = bindingResult.getFieldErrors().stream()
                    .map(f -> f.getField().concat(f.getDefaultMessage()))
                    .collect(Collectors.joining(","));
        }

        return CommonRes.failure(GlobalEnum.PARAMS_ERROR, message);
    }

    /**
     * 异常 对应 注解
     * ConstraintViolationException controller层类上面添加@Validated  方法参数@NotNull类型注解
     * 这两个注解都是实现JSR-303规范，不同的是@Validated是spring的注解支持groups以及可以用在spring mvc处理器的方法级别入参验证 ，@Valid是Javax提供的注解，可以支持多个bean嵌套验证。
     * @param request 请求体
     * @param e 校验器异常
     * @return 返回结果集
     */
    @ExceptionHandler({ConstraintViolationException.class})
    public CommonRes<Object> handleValidException(HttpServletRequest request, ConstraintViolationException e) {
        log.error("错误请求url = {}, exception = {}", request.getRequestURI(), e);
        String message = null;

        Set<ConstraintViolation<?>> constraintViolations = ((ConstraintViolationException) e).getConstraintViolations();
        message = constraintViolations.stream()
                .map(c -> {
                    String path = c.getPropertyPath().toString();
                    return path.substring(path.lastIndexOf(".") + 1).concat(c.getMessage());
                })
                .collect(Collectors.joining(","));;
        return CommonRes.failure(GlobalEnum.PARAMS_ERROR, message);
    }




}
